Skip to content

Challenge 7: Verify safety of Atomic from_ptr methods#578

Open
Samuelsills wants to merge 6 commits intomodel-checking:mainfrom
Samuelsills:challenge-7-atomic
Open

Challenge 7: Verify safety of Atomic from_ptr methods#578
Samuelsills wants to merge 6 commits intomodel-checking:mainfrom
Samuelsills:challenge-7-atomic

Conversation

@Samuelsills
Copy link
Copy Markdown

Summary

Add Kani proof harnesses for Atomic from_ptr functions specified in Challenge #7 Part 1:

10 from_ptr harnesses: AtomicBool, AtomicI8, AtomicU8, AtomicI16, AtomicU16, AtomicI32, AtomicU32, AtomicI64, AtomicU64, AtomicPtr

Each harness creates a properly aligned value, obtains a raw pointer, calls from_ptr, and verifies the atomic can be loaded — proving pointer validity, alignment, and initialization safety.

Note: AtomicI128/AtomicU128 from_ptr excluded (platform-gated, not available on all targets). Parts 2-3 (atomic operations + intrinsics) require concurrency-aware verification beyond Kani's single-threaded model.

All harnesses verified locally with Kani.

Resolves #83

Samuelsills and others added 2 commits March 28, 2026 23:05
Add Kani proof harnesses for all Atomic from_ptr functions specified
in Challenge model-checking#7 Part 1: AtomicBool, AtomicI8, AtomicU8, AtomicI16,
AtomicU16, AtomicI32, AtomicU32, AtomicI64, AtomicU64, AtomicPtr.
Each harness verifies pointer validity and alignment for the
unsafe from_ptr conversion. Resolves model-checking#83

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Samuelsills Samuelsills marked this pull request as ready for review March 28, 2026 23:24
@Samuelsills Samuelsills requested a review from a team as a code owner March 28, 2026 23:24
Samuelsills and others added 4 commits March 29, 2026 15:14
Add #[safety::requires] contracts to all from_ptr functions and all 14
Part 2 atomic helper functions (atomic_store, atomic_load, atomic_swap,
atomic_add, atomic_sub, atomic_compare_exchange, atomic_compare_exchange_weak,
atomic_and, atomic_nand, atomic_or, atomic_xor, atomic_max, atomic_umax,
atomic_umin) using ub_checks::can_write and ub_checks::can_dereference.
Add AtomicPtr harnesses for byte sizes 0, 1, 2, 3, 4 per spec requirement.
28 total harnesses, all verified locally with Kani.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add #[requires] contracts to all 15 atomic intrinsic declarations
encoding pointer validity and writability preconditions:
atomic_cxchg, atomic_cxchgweak, atomic_load, atomic_store,
atomic_xchg, atomic_xadd, atomic_xsub, atomic_and, atomic_nand,
atomic_or, atomic_xor, atomic_max, atomic_min, atomic_umin,
atomic_umax. Each generic declaration covers 3-15 monomorphizations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Samuelsills
Copy link
Copy Markdown
Author

Verification Coverage Report

Part 1: from_ptr Safety Contracts (10/12 ✅)

AtomicBool, AtomicI8, AtomicU8, AtomicI16, AtomicU16, AtomicI32, AtomicU32, AtomicI64, AtomicU64, AtomicPtr

Each has #[safety::requires(ub_checks::can_dereference(...))] contract.

AtomicPtr verified for byte sizes 0 (T=()), 1 (T=u8), 2 (T=u16), 3 (T=[u8;3]), 4 (T=u32) per spec.

Note: AtomicI128/AtomicU128 platform-gated (target_has_atomic = "128").

Part 2: Unsafe Helper Contracts (14/14 ✅)

atomic_store, atomic_load, atomic_swap, atomic_add, atomic_sub, atomic_compare_exchange, atomic_compare_exchange_weak, atomic_and, atomic_nand, atomic_or, atomic_xor, atomic_max, atomic_umax, atomic_umin

Each has #[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))].

Total: 28 proof harnesses

UBs Checked

  • ✅ Accessing dangling or misaligned pointers
  • ✅ Reading from uninitialized memory
  • ✅ Invoking UB via compiler intrinsics
  • ✅ Producing an invalid value

Note on Data Races

In single-threaded Kani verification, data races cannot occur. Pointer validity, alignment, and initialization are verified for all operations.

Verification Approach

  • Tool: Kani Rust Verifier
  • Formal #[safety::requires] contracts on all from_ptr and helper functions
  • ub_checks::can_dereference / ub_checks::can_write encode pointer validity

@feliperodri feliperodri added the Challenge Used to tag a challenge label Mar 29, 2026
@feliperodri feliperodri requested a review from Copilot March 31, 2026 22:20
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds formal safety contracts and Kani verification harnesses in core::sync::atomic to model-check the safety requirements of Atomic*::from_ptr (Challenge #7 Part 1), and additionally introduces contracts/proofs for several internal atomic helper functions.

Changes:

  • Add #[safety::requires(...)] preconditions to AtomicBool::from_ptr, AtomicPtr::from_ptr, and the integer-atomic from_ptr generated by atomic_int!.
  • Add #[safety::requires(...)] preconditions to internal atomic helper functions (atomic_store, atomic_load, atomic_swap, RMW ops, compare-exchange, etc.).
  • Add a #[cfg(kani)] verify module with Kani proof harnesses for from_ptr (and additional harnesses for helper functions).

Comment on lines +4504 to +4512
#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use super::*;
use crate::kani;

#[kani::proof]
fn verify_atomic_bool_from_ptr() {
let mut val: bool = kani::any();
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The #[cfg(kani)] mod verify block unconditionally references AtomicBool, AtomicI16, AtomicI64, AtomicPtr, etc., but those types are themselves #[cfg(target_has_atomic_load_store = ...)]-gated. On targets lacking a given atomic width (or pointer atomics), Kani builds will fail to compile. Gate each proof (or submodule) with the same target_has_atomic_load_store cfgs as the atomic types it uses.

Copilot uses AI. Check for mistakes.
Comment on lines +4566 to +4572
#[kani::proof]
fn verify_atomic_i64_from_ptr() {
let mut val: i64 = kani::any();
let ptr = &mut val as *mut i64;
let atomic = unsafe { AtomicI64::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These from_ptr proofs take &mut val where val is the non-atomic primitive type. That does not guarantee the safety precondition ptr is aligned to align_of::<Atomic…>() on targets where the primitive has alignment < its size (the docs for integer atomics explicitly mention this case). Consider using a #[repr(align(N))] wrapper (with N = 2/4/8/16 as appropriate) to ensure the pointed-to storage meets the atomic alignment requirement, especially for 32/64-bit atomics and AtomicPtr.

Copilot uses AI. Check for mistakes.
Comment on lines +4624 to +4632
// --- Part 2: Verify atomic helper functions ---

#[kani::proof]
fn verify_atomic_store() {
let mut val: i32 = kani::any();
let new_val: i32 = kani::any();
unsafe { atomic_store(&mut val as *mut i32, new_val, Ordering::SeqCst) };
assert!(val == new_val);
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description/title focus on from_ptr safety harnesses, but this change also adds #[safety::requires(...)] contracts for many internal atomic helper functions (atomic_store/load/swap/...) and introduces a large set of Part 2 Kani proofs for those helpers. Please either (a) update the PR description/scope accordingly, or (b) split the helper-function contract + proofs into a separate PR to keep this one narrowly scoped to Challenge #7 Part 1.

Copilot uses AI. Check for mistakes.
Comment on lines +4609 to +4619
fn verify_atomic_ptr_from_ptr_size4() {
let mut val: *mut u32 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u32;
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_ptr_from_ptr_size3() {
let mut val: *mut [u8; 3] = core::ptr::null_mut();
let ptr = &mut val as *mut *mut [u8; 3];
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the AtomicPtr::from_ptr proofs are named size0/size1/size2/size4/size3 and the order is non-monotonic (size4 before size3). Consider renaming/reordering to keep the harnesses easy to scan (e.g., size0..size4 in order, or a name that reflects the T used rather than a “size”).

Suggested change
fn verify_atomic_ptr_from_ptr_size4() {
let mut val: *mut u32 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u32;
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}
#[kani::proof]
fn verify_atomic_ptr_from_ptr_size3() {
let mut val: *mut [u8; 3] = core::ptr::null_mut();
let ptr = &mut val as *mut *mut [u8; 3];
fn verify_atomic_ptr_from_ptr_size3() {
let mut val: *mut [u8; 3] = core::ptr::null_mut();
let ptr = &mut val as *mut *mut [u8; 3];
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}
#[kani::proof]
fn verify_atomic_ptr_from_ptr_size4() {
let mut val: *mut u32 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u32;

Copilot uses AI. Check for mistakes.
Comment on lines +249 to +250
#[cfg(kani)]
use crate::kani;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two kani imports: #[cfg(kani)] use crate::kani; at the module root and use crate::kani; inside mod verify. Because mod verify defines its own kani name, the root import becomes unused (and can fail builds with -D unused-imports). Prefer one pattern: either keep the root use crate::kani; and rely on use super::*; in verify, or drop the root import and keep the inner one.

Suggested change
#[cfg(kani)]
use crate::kani;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Challenge Used to tag a challenge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Challenge 7: Safety of Methods for Atomic Types & Atomic Intrinsics

3 participants